package com.cauli.file.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.StrUtil;
import com.cauli.file.component.FileComponent;
import com.cauli.file.component.FileDealComp;
import com.cauli.file.feignService.ManageFeignService;
import com.cauli.file.feignService.model.StorageVO;
import com.cauli.file.model.dto.file.BatchDownloadFileDTO;
import com.cauli.file.model.dto.file.DownloadFileDTO;
import com.cauli.file.model.dto.file.PreviewDTO;
import com.cauli.file.model.dto.file.UploadFileDTO;
import com.cauli.file.model.entity.FileEntity;
import com.cauli.file.model.entity.UserFile;
import com.cauli.file.model.vo.file.UploadFileVO;
import com.cauli.file.service.FileService;
import com.cauli.file.service.FileTransferService;
import com.cauli.file.service.UserFileService;
import com.cauli.utils.util.ResultResponse;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSort;
import com.qiwenshare.common.anno.MyLog;
import com.qiwenshare.common.util.MimeUtils;
import com.qiwenshare.ufop.exception.operation.DownloadException;
import com.qiwenshare.ufop.factory.UFOPFactory;
import com.qiwenshare.ufop.operation.download.Downloader;
import com.qiwenshare.ufop.operation.download.domain.DownloadFile;
import com.qiwenshare.ufop.operation.download.domain.Range;
import com.qiwenshare.ufop.util.UFOPUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author Cauli
 * @date 2023-03-18 12:00
 * @description 文件传输 前端控制器
 */
@Api(tags = "文件传输")
@ApiSort(2)
@RestController
@RequestMapping("/fileTransfer")
@Slf4j
public class FileTransferController {
    @Autowired
    private FileTransferService filetransferService;

    @Autowired
    private FileService fileService;

    @Autowired
    private UserFileService userFileService;

    @Autowired
    private ManageFeignService manageFeignService;

    @Resource
    private FileDealComp fileDealComp;

    @Resource
    private UFOPFactory ufopFactory;

    @Value("${ufop.storage-type}")
    private Integer storageType;

    @Value("${ufop.local-storage-path}")
    private String localStoragePath;

    public static final String CURRENT_MODULE = "文件传输";

    @MyLog(operation = "极速上传", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "极速上传", notes = "校验文件MD5判断文件是否存在，如果存在直接上传成功并返回skipUpload=true，如果不存在返回skipUpload=false需要再次调用该接口的POST方法")
    @GetMapping("/uploadFile")
    public ResultResponse<UploadFileVO> uploadFileSpeed(UploadFileDTO uploadFileDTO) {
        // 检查存储源空间大小是否充足
        long userId = StpUtil.getLoginIdAsLong();
        Long totalStorageSize = manageFeignService.getTotalStorageSize(userId).getData();
        Long storageSize = filetransferService.selectStorageSizeByUserId(userId);
        if (storageSize == null) {
            storageSize = 0L;
        }
        boolean isCheckSuccess = storageSize + uploadFileDTO.getTotalSize() <= totalStorageSize;

        if (!isCheckSuccess) {
            return ResultResponse.fail().message("存储空间不足");
        }
        UploadFileVO uploadFileVo = filetransferService.uploadFileSpeed(uploadFileDTO);
        return ResultResponse.success().data(uploadFileVo);
    }

    @MyLog(operation = "上传文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 2)
    @ApiOperation(value = "上传文件", notes = "真正的上传文件接口")
    @PostMapping("/uploadFile")
    public ResultResponse<UploadFileVO> uploadFile(HttpServletRequest request, UploadFileDTO uploadFileDTO) {
        long userId = StpUtil.getLoginIdAsLong();
        filetransferService.uploadFile(request, uploadFileDTO, userId);
        UploadFileVO uploadFileVo = new UploadFileVO();
        return ResultResponse.success().data(uploadFileVo);
    }

    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "获得下载文件下载链接")
    @GetMapping("/getFileUrl/{fileName}")
    public void download(HttpServletResponse httpServletResponse, @PathVariable String fileName) {
        File file;
        String userFileId = fileName.substring(0, fileName.lastIndexOf('.'));
        UserFile userFile = userFileService.getById(userFileId);
        FileEntity fileEntity = fileService.getById(userFile.getFileId());
        if (fileEntity.getStorageType() == 0) {
            // 存储元类型为本地

            if (StrUtil.isNotBlank(localStoragePath)) {
                // 文件存储路径不为空
                file = new File(localStoragePath + File.separator + fileEntity.getFileUrl());
            } else {
                // 文件存储路径为空
                String classpath = Objects.requireNonNull(getClass().getResource("/")).getPath();
                file = new File(classpath + "static/" + fileEntity.getFileUrl());
            }
            try (
                    // 读取文件的字节流
                    FileInputStream fileInputStream = new FileInputStream(file);
                    // 将文件写入输入流
                    InputStream inputStream = new BufferedInputStream(fileInputStream);
                    // 设置输出流的格式
                    ServletOutputStream outputStream = httpServletResponse.getOutputStream();
            ) {
                IOUtils.copyLarge(inputStream, outputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            // 存储源类型为阿里云OSS

            // 设置文件名
            fileName = userFile.getFileName() + "." + userFile.getExtendName();
            try {
                fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileName);

            // 下载文件
            Downloader downloader = ufopFactory.getDownloader(fileEntity.getStorageType());
            if (downloader == null) {
                log.error("下载失败，文件存储类型不支持下载，storageType:{}", fileEntity.getStorageType());
                throw new DownloadException("下载失败");
            }
            httpServletResponse.setContentLengthLong(fileEntity.getFileSize());
            DownloadFile downloadFile = new DownloadFile();
            downloadFile.setFileUrl(fileEntity.getFileUrl());
            downloader.download(httpServletResponse, downloadFile);
        }
    }

    @MyLog(operation = "下载文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 4)
    @ApiOperation(value = "下载文件")
    @GetMapping("/downloadFile")
    public void downloadFile(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, DownloadFileDTO downloadFileDTO) {
        String token = StpUtil.getTokenValue();
        boolean authResult = fileDealComp.checkAuthDownloadAndPreview(downloadFileDTO.getShareBatchNum(),
                downloadFileDTO.getExtractionCode(),
                token,
                downloadFileDTO.getUserFileId(), null);
        if (!authResult) {
            log.error("没有权限下载！！！");
            return;
        }
        // 设置强制下载不打开
        httpServletResponse.setContentType("application/force-download");
        UserFile userFile = userFileService.getById(downloadFileDTO.getUserFileId());
        String fileName = "";
        if (userFile.getIsDir() == 1) {
            fileName = userFile.getFileName() + ".zip";
        } else {
            fileName = userFile.getFileName() + "." + userFile.getExtendName();

        }
        try {
            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 设置文件名
        httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileName);

        filetransferService.downloadFile(httpServletResponse, downloadFileDTO);
    }

    @MyLog(operation = "批量下载文件", module = CURRENT_MODULE)
    @ApiOperationSupport(order = 5)
    @ApiOperation(value = "批量下载文件")
    @GetMapping("/batchDownloadFile")
    public void batchDownloadFile(HttpServletResponse httpServletResponse, BatchDownloadFileDTO batchDownloadFileDTO) {
        String files = batchDownloadFileDTO.getUserFileIds();
        String[] userFileIdStrs = files.split(",");
        List<Long> userFileIds = new ArrayList<>();
        for (String userFileId : userFileIdStrs) {
            UserFile userFile = userFileService.getById(userFileId);
            if (userFile.getIsDir() == 0) {
                userFileIds.add(Long.valueOf(userFileId));
            } else {
                FileComponent fileComponent = new FileComponent(userFile.getFilePath(), userFile.getFileName(), true);
                List<UserFile> userFileList = userFileService.selectUserFileByLikeRightFilePath(fileComponent.getPath(), userFile.getUserId());
                List<Long> userFileIds1 = userFileList.stream().map(UserFile::getUserFileId).collect(Collectors.toList());
                userFileIds.add(userFile.getUserFileId());

                userFileIds.addAll(userFileIds1);
            }
        }
        UserFile userFile = userFileService.getById(userFileIdStrs[0]);
        // 设置强制下载不打开
        httpServletResponse.setContentType("application/force-download");
        Date date = new Date();
        String fileName = String.valueOf(date.getTime());
        // 设置文件名
        httpServletResponse.addHeader("Content-Disposition", "attachment;fileName=" + fileName + ".zip");
        filetransferService.downloadUserFileList(httpServletResponse, userFile.getFilePath(), fileName, userFileIds);
    }

    @ApiOperationSupport(order = 6)
    @ApiOperation(value = "预览文件")
    @GetMapping("/preview")
    public void preview(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, PreviewDTO previewDTO) throws IOException {
        if (previewDTO.getPlatform() != null && previewDTO.getPlatform() == 1) {
            filetransferService.previewPictureFile(httpServletResponse, previewDTO);
            return;
        }
        String token = StpUtil.getTokenValue();
        UserFile userFile = userFileService.getById(previewDTO.getUserFileId());
        boolean authResult = fileDealComp.checkAuthDownloadAndPreview(previewDTO.getShareBatchNum(),
                previewDTO.getExtractionCode(),
                token,
                previewDTO.getUserFileId(),
                previewDTO.getPlatform());

        if (!authResult) {
            log.error("没有权限预览！！！");
            return;
        }

        String fileName = userFile.getFileName() + "." + userFile.getExtendName();
        try {
            fileName = new String(fileName.getBytes("utf-8"), "ISO-8859-1");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 设置文件名
        httpServletResponse.addHeader("Content-Disposition", "fileName=" + fileName);
        String mime = MimeUtils.getMime(userFile.getExtendName());
        httpServletResponse.setHeader("Content-Type", mime);
        if (UFOPUtils.isImageFile(userFile.getExtendName())) {
            httpServletResponse.setHeader("cache-control", "public");
        }

        FileEntity fileEntity = fileService.getById(userFile.getFileId());
        if (UFOPUtils.isVideoFile(userFile.getExtendName()) || "mp3".equalsIgnoreCase(userFile.getExtendName()) || "flac".equalsIgnoreCase(userFile.getExtendName())) {
            // 获取从那个字节开始读取文件
            String rangeString = httpServletRequest.getHeader("Range");
            int start = 0;
            if (StringUtils.isNotBlank(rangeString)) {
                start = Integer.parseInt(rangeString.substring(rangeString.indexOf("=") + 1, rangeString.indexOf("-")));
            }

            Downloader downloader = ufopFactory.getDownloader(fileEntity.getStorageType());
            DownloadFile downloadFile = new DownloadFile();
            downloadFile.setFileUrl(fileEntity.getFileUrl());
            Range range = new Range();
            range.setStart(start);

            if (start + 1024 * 1024 * 1 >= fileEntity.getFileSize().intValue()) {
                range.setLength(fileEntity.getFileSize().intValue() - start);
            } else {
                range.setLength(1024 * 1024 * 1);
            }
            downloadFile.setRange(range);
            InputStream inputStream = downloader.getInputStream(downloadFile);

            OutputStream outputStream = httpServletResponse.getOutputStream();
            try {
                // 返回码需要为206，代表只处理了部分请求，响应了部分数据
                httpServletResponse.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                // 每次请求只返回1MB的视频流
                httpServletResponse.setHeader("Accept-Ranges", "bytes");
                // 设置此次相应返回的数据范围
                httpServletResponse.setHeader("Content-Range", "bytes " + start + "-" + (fileEntity.getFileSize() - 1) + "/" + fileEntity.getFileSize());
                IOUtils.copy(inputStream, outputStream);
            } finally {
                IOUtils.closeQuietly(inputStream);
                IOUtils.closeQuietly(outputStream);
                if (downloadFile.getOssClient() != null) {
                    downloadFile.getOssClient().shutdown();
                }
            }
        } else {
            filetransferService.previewFile(httpServletResponse, previewDTO);
        }
    }

    @ApiOperationSupport(order = 7)
    @ApiOperation(value = "获取存储信息")
    @GetMapping("/getStorage")
    public ResultResponse<StorageVO> getStorage() {
        StorageVO storageVO = new StorageVO();
        Long userId = StpUtil.getLoginIdAsLong();
        storageVO.setUserId(userId);
        storageVO.setStorageSize(filetransferService.selectStorageSizeByUserId(userId));
        storageVO.setTotalStorageSize(manageFeignService.getTotalStorageSize(userId).getData());

        return ResultResponse.success().data(storageVO);
    }
}
